package com.dobbinsoft.iotechn.user.service.open;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.dobbinsoft.iotechn.core.constant.Const;
import com.dobbinsoft.iotechn.core.exception.ServiceException;
import com.dobbinsoft.iotechn.core.exception.ThirdPartServiceException;
import com.dobbinsoft.iotechn.core.model.UserSession;
import com.dobbinsoft.iotechn.core.util.GeneratorUtil;
import com.dobbinsoft.iotechn.core.util.SessionUtil;
import com.dobbinsoft.iotechn.data.client.SMSClient;
import com.dobbinsoft.iotechn.data.client.SMSResult;
import com.dobbinsoft.iotechn.data.compent.CacheComponent;
import com.dobbinsoft.iotechn.user.api.open.UserService;
import com.dobbinsoft.iotechn.user.constant.UserConst;
import com.dobbinsoft.iotechn.user.domain.UserDO;
import com.dobbinsoft.iotechn.user.dto.UserDTO;
import com.dobbinsoft.iotechn.user.exception.UserExceptionDefinition;
import com.dobbinsoft.iotechn.user.exception.UserServiceException;
import com.dobbinsoft.iotechn.user.mapper.UserMapper;
import com.dobbinsoft.iotechn.user.properties.FrameworkWxProperties;
import com.dobbinsoft.iotechn.core.enums.StatusType;
import okhttp3.OkHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * Created by rize on 2019/6/30.
 */
@Service("userService")
public class UserServiceImpl implements UserService {

    private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private SMSClient smsClient;

    @Autowired
    private CacheComponent cacheComponent;

    @Autowired
    private StringRedisTemplate userRedisTemplate;

    private OkHttpClient okHttpClient = new OkHttpClient();

    @Autowired
    private FrameworkWxProperties frameworkWxProperties;

    @Override
    public String sendVerifyCode(String phone) throws ServiceException {
        String verifyCode = GeneratorUtil.genSixVerifyCode();
        SMSResult res = smsClient.sendRegisterVerify(phone, verifyCode);
        if (res.isSucc()) {
            cacheComponent.putRaw(UserConst.CA_USER_SEND_SMS_VERIFY + phone, verifyCode, 300);
            return "ok";
        } else {
            throw new ThirdPartServiceException(res.getMsg(), UserExceptionDefinition.USER_SEND_VERIFY_FAILED.getCode());
        }
    }

    @Override
    @DS(Const.DB_M)
    @Transactional(rollbackFor = Exception.class)
    public UserDTO fastLogin(String phone, String verifyCode, String ip) throws ServiceException {
        // 1.校验验证码
        String verifyCodeFromCache = cacheComponent.getRaw(UserConst.CA_USER_SEND_SMS_VERIFY + phone);
        if (StringUtils.isEmpty(verifyCodeFromCache)) {
            throw new UserServiceException(UserExceptionDefinition.USER_VERIFY_CODE_NOT_EXIST);
        }
        // 2.先尝试登录
        Date now = new Date();
        UserDTO user = userMapper.login(phone);
        if (user != null) {
            // 2.1. 生成访问Token
            this.setAccessToken(user);
            // 2.2. 更新用户最后登录状态
            UserDO updateUserDO = new UserDO();
            updateUserDO.setId(user.getId());
            updateUserDO.setLastLoginIp(ip);
            updateUserDO.setGmtLastLogin(now);
            userMapper.updateById(updateUserDO);
            return user;
        }
        // 3.尝试注册
        user = new UserDTO();
        // 3.1. 注册此用户
        UserDO userDO = new UserDO();
        userDO.setPhone(phone);
        userDO.setLastLoginIp(ip);
        userDO.setStatus(StatusType.ACTIVE.getCode());
        userDO.setGmtLastLogin(now);
        userDO.setGmtCreate(now);
        userDO.setGmtUpdate(now);
        userMapper.insert(userDO);
        BeanUtils.copyProperties(userDO, user);
        // 3.2. 生成AccessToken
        this.setAccessToken(user);
        return user;
    }

    /**
     * 设置用户访问Token抽取方法
     * @param user
     */
    private void setAccessToken(UserDTO user) {
        String accessToken = user.getId() + "-" +GeneratorUtil.genUUId();
        user.setAccessToken(accessToken);
        userRedisTemplate.opsForValue().set(Const.USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(user),30, TimeUnit.MINUTES);
    }

    @Override
    public String logout(String accessToken) throws ServiceException {
        userRedisTemplate.delete(Const.USER_REDIS_PREFIX + accessToken);
        return "ok";
    }

    @Override
    @DS(Const.DB_M)
    @Transactional(rollbackFor = Exception.class)
    public String syncUserInfo(String nickname, String avatarUrl, Integer gender, String accessToken, Long userId) throws ServiceException {
        UserDO updateUserDO = new UserDO();
        updateUserDO.setId(userId);
        updateUserDO.setNickname(nickname);
        updateUserDO.setAvatarUrl(avatarUrl);
        updateUserDO.setGmtUpdate(new Date());
        if (userMapper.updateById(updateUserDO) > 0) {
            //更新SESSION缓存
            UserSession user = SessionUtil.getUser();
            if (!StringUtils.isEmpty(nickname)) {
                user.setNickname(nickname);
            }
            if (!StringUtils.isEmpty(avatarUrl)) {
                user.setAvatarUrl(avatarUrl);
            }
            if (gender != null) {
                user.setGender(gender);
            }
            userRedisTemplate.opsForValue().set(Const.USER_REDIS_PREFIX + accessToken, JSONObject.toJSONString(user));
            return "ok";
        }
        throw new UserServiceException(UserExceptionDefinition.USER_UNKNOWN_EXCEPTION);
    }

    @Override
    public Boolean checkLogin(String accessToken) throws ServiceException {
        return userRedisTemplate.hasKey(Const.USER_REDIS_PREFIX + accessToken);
    }

}
